#ifndef MAP_H
#define MAP_H

// Libraries
#include <irrlicht.h>

// Namespaces
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

const int MAP_TILE_WIDTH	= 32;
const int MAP_TILE_HEIGHT	= 32;

// Structures
struct TileStruct 
{
	TileStruct() : Texture(NULL), Zone(0), EventType(0), EventData(0), Wall(false), PVP(true) { }
	ITexture *Texture;
	int Zone;
	int EventType, EventData;
	bool Wall;
	bool PVP;
};

struct IndexedEventStruct 
{
	IndexedEventStruct(const TileStruct *TTile, const position2di &TPosition) : Tile(TTile), Position(TPosition) { }
	const TileStruct *Tile;
	position2di Position;
};

// Forward Declarations
class CGObject;
class PlayerClass;
class PacketClass;

// Classes
class CGMap 
{
	public:
		enum EventType 
		{
			EVENT_NONE,
			EVENT_SPAWN,
			EVENT_MAPCHANGE,
			EVENT_VENDOR,
			EVENT_TRADER,
			EVENT_COUNT
		};

		CGMap(const stringc &TFilename, int TWidth, int THeight);
		CGMap(const stringc &TFilename);
		CGMap(int TMapID);
		~CGMap();

		void Update(u32 TDeltaTime);

		// States
		int GetID() const { return ID; }
		const stringc &GetFilename() const { return Filename; }

		// Graphics
		void Render();
		void RenderForMapEditor(bool TDrawWall, bool TDrawZone, bool TDrawPVP);

		void SetCameraScroll(const position2di &TPosition);
		const position2di &GetCameraScroll() const { return m_CameraScroll; }
		const dimension2di &GetViewSize() const { return m_ViewSize; }

		bool GridToScreen(const position2di &TGridPosition, position2di &TScreenPosition) const;
		bool ScreenToGrid(const position2di &TScreenPosition, position2di &TGridPosition);

		// Collision
		bool CanMoveTo(const position2di &TPosition);

		// Object management
		void AddObject(CGObject *TObject);
		void RemoveObject(CGObject *TObject);
		const list<CGObject *> &GetObjects() const;
		void GetClosePlayers(const PlayerClass *TPlayer, float TDistanceSquared, list<PlayerClass *> &TPlayers);
		PlayerClass *GetClosestPlayer(const PlayerClass *TPlayer, float TMaxDistanceSquared, int TState);

		void SendPacketToPlayers(PacketClass *TPacket, PlayerClass *ExceptionPlayer=NULL);

		// Events
		IndexedEventStruct *GetIndexedEvent(int TEventType, int TEventData);

		// Map editing
		int GetWidth() const { return Width; }
		int GetHeight() const { return Height; }
		bool IsValidPosition(int TX, int TY) const { return TX >= 0 && TY >= 0 && TX < Width && TY < Height; }
		void SetNoZoneTexture(ITexture *TTexture) { NoZoneTexture = TTexture; }

		void GetTile(int TX, int TY, TileStruct &TTile) const { TTile = Tiles[TX][TY]; }
		const TileStruct *GetTile(int TX, int TY) const { return &Tiles[TX][TY]; }
		void SetTile(int TX, int TY, const TileStruct *TTile) { Tiles[TX][TY] = *TTile; }

		// File IO
		int SaveMap();
		int LoadMap();

	private:

		void Init();

		void AllocateMap();
		void FreeMap();

		void SendObjectUpdates();

		void GetTextureListFromMap(array<ITexture *> &TTextures);
		int GetTextureIndex(array<ITexture *> &TTextures, ITexture *TTexture);

		// Map file
		int ID;
		stringc Filename;

		// Viewing
		dimension2di m_ViewSize;
		position2di  m_CameraScroll;
		position2di  m_CursorPos;
		//TODO: Remove
		position2di  m_SectionPos;
		int			 m_SectionType;
		int			 m_SectionPart;

		// Map data
		TileStruct **Tiles;
		int Width, Height;

		// Textures
		ITexture *NoZoneTexture;
		array<ITexture *> Textures;

		// Events
		array<IndexedEventStruct> IndexedEvents;

		// Objects
		u32 ObjectUpdateTime;
		list<CGObject *> m_GObjectList;
};

#endif
